//
//  GameObject.m
//  Agility
//
//  Created by Dominique d'Argent on 15.03.11.
//  Copyright 2011 Nicky Nubbel. All rights reserved.
//

#import "GameObject.h"
#import "GameObjectLayer.h"

const NSTimeInterval kGameObjectDefaultDuration = 0.25;

@implementation GameObject

@synthesize gameObjectKey;
@synthesize lifeTime;
@synthesize maxLifeTime;
@synthesize spawnTime;
@synthesize position;
@synthesize destination;
@synthesize destinationAngle;
@synthesize trajectory;
@synthesize angle;
@synthesize velocity;
@synthesize angularVelocity;
@synthesize visible;
@synthesize durationForMovement;
@synthesize durationForRotation;
@synthesize imageName;
@synthesize layer;

#pragma mark - Object lifecycle

//
// initWithImageNamed:
//
// Creates a new game object.
//
- (id)initWithImageNamed:(NSString *)newImageName {
    self = [super init];
    
    if (self) {
        changes = 1;
        imageName = newImageName;
        maxLifeTime = DBL_MAX;
        visible = YES;
    }
    
    return self;
}

//
// dealloc
//
// Releases retained objects.
//
- (void)dealloc {
    [gameObjectKey release];
    [imageName release];
    [layer release];
    
    [super dealloc];
}

#pragma mark - Update mechanism

//
// hasChanges
//
// Returns YES if the game object has changed since the last update.
// NO otherwise.
//
- (BOOL)hasChanges {
    return changes > 0;
}

//
// resetChanges
//
// Resets the changes counter.
//
- (void)resetChanges {
    changes = 0;
}

//
// updateLifeTimeWithTimeInterval:
//
// Updates the life time given a time interval.
//
// Returns YES if the life time exceeds the maximum life time. NO otherwise.
//
- (BOOL)updateLifeTimeWithTimeInterval:(NSTimeInterval)timeInterval {
    lifeTime += timeInterval;
    
    if (lifeTime >= maxLifeTime) {
        changes++;
        
        return YES;
    }
    
    return NO;

}

//
// updatePositionWithTimeInterval:
//
// Calculates the new position based on a given time interval, velocity and
// trajectory.
//
- (void)updatePositionWithTimeInterval:(NSTimeInterval)timeInterval {
  CGFloat x = position.x;
    CGFloat y = position.y;
    
    x += timeInterval * velocity * sinf(trajectory);
    y += timeInterval * velocity * cosf(trajectory);
    
    position = CGPointMake(x, y);

    changes++;
}

//
// updateTransformWithTimeInterval:
//
// Updates transform properties such as rotation or scale given a time interval.
//
- (void)updateTransformWithTimeInterval:(NSTimeInterval)timeInterval {
    angle += angularVelocity * timeInterval;
    
    changes++;

}

//
// updateWithTimeInterval:
//
// Updates the object's properties given the elapsed time.
// This method should not interact with existing GameObjects.
//
// Returns YES if object data should be removed. NO otherwise.
//
- (BOOL)updateWithTimeInterval:(NSTimeInterval)timeInterval {
    if ([self updateLifeTimeWithTimeInterval: timeInterval]) {
        return YES;
    }

    [self updatePositionWithTimeInterval: timeInterval];
    [self updateTransformWithTimeInterval: timeInterval];
    
    return NO;
}

//
// updateWithValue:
//
// Updates the object's properties given a specific value (by default does 
// nothing).
//
- (void)updateWithValue:(NSValue *)value {
    changes++;
}

//
// collidesWithGameObject:
//
// Returns YES if this game object collides with a given test object.
// NO otherwise.
//
- (BOOL)collidesWithGameObject:(GameObject *)testObject {
    //return CGRectIntersectsRect(self.currentFrame, testObject.currentFrame);
    
    return [self distanceToPoint:testObject.currentPosition] <= self.collisionRadius + testObject.collisionRadius;
}

//
// didCollideWithGameObject
//
// Called when collision with a given object was detected.
//
// Returns YES if object data should be removed. NO otherwise.
//
- (BOOL)didCollideWithGameObject:(GameObject *)collidingObject {
    return YES;
}

#pragma mark - Property accessors

//
// size
//
// Accessor for the size.
//
- (CGSize)size {
    return layer.size;
}

//
// currentPosition
//
// Accessor for the object's current position.
//
- (CGPoint)currentPosition {
    return ((CALayer *)[layer presentationLayer]).position;
}

//
// currentFrame
//
// Accessor for the object's current frame.
//
- (CGRect)currentFrame {
    CGRect theFrame = ((CALayer *)[layer presentationLayer]).frame;
    theFrame.size = layer.size;
    
    return theFrame;
}

//
// currentAngle
//
// Accessor for the object's current angle
//
- (CGFloat)currentAngle {
    CGFloat theAngle = -[[[layer presentationLayer] valueForKeyPath:@"transform.rotation"] floatValue];
    
    if (theAngle < 0) {
        theAngle += 2 * M_PI;
    }
    
    return theAngle;
}

//
// collisionRadius
//
// Accessor for the object's radius used for collision detection.
//
- (CGFloat)collisionRadius {
    return (layer.size.width + layer.size.height) / 4;
}

#pragma mark - Geometric calculations

//
// angleToPoint:
//
// Calculates the angle to a given point in radians.
//
- (CGFloat)angleToPoint:(CGPoint)thePoint {
    CGFloat theAngle = atan2f(thePoint.x - self.currentPosition.x, thePoint.y - self.currentPosition.y);
    
    if (theAngle < 0) {
        theAngle += 2 * M_PI;
    }
    
    return theAngle;
}

//
// angularDistanceToAngle
//
// Calculates the angular distance to a given angle in radians.
//
- (CGFloat)angularDistanceToAngle:(CGFloat)theAngle {
    CGFloat diff = theAngle - self.currentAngle;
    
    if (diff > M_PI) {
        diff -= 2 * M_PI;
    }
    else if (diff < -M_PI) {
        diff += 2 * M_PI;
    }
    
    return diff;
}

//
// distanceToPoint
//
// Calculates the distance to a given point.
//
- (CGFloat)distanceToPoint:(CGPoint)thePoint {
    CGFloat dx = thePoint.x - self.currentPosition.x;
    CGFloat dy = thePoint.y - self.currentPosition.y;
    
    return sqrtf(dx*dx + dy*dy);
}


//
// durationForMovementToPoint:
//
// Calculates the time it takes to move the game object to a given point.
//
- (NSTimeInterval)durationForMovementToPoint:(CGPoint)thePoint {
    return [self distanceToPoint:thePoint] / velocity;
}

//
// durationForRotationToAngle:
//
// Calculates the time it takes to rotate the game object to a given angle.
//
- (NSTimeInterval)durationForRotationToAngle:(CGFloat)theAngle {
    return fabs([self angularDistanceToAngle:theAngle]) / angularVelocity;
}

@end
